package com.beta.backend.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.beta.backend.gateway.config.GatewayCfgBean;
import com.beta.backend.gateway.utils.JwtUtil;
import com.beta.cat.service.RedissonService;
import com.beta.platform.rest.auth.LoginAccountDetail;
import com.beta.platform.rest.auth.ThirdUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Base64;
import java.util.Map;

/**
 * @description  授权认证过滤器，用于校验用户信息
 * @author: zdwang
 * @date 2021/12/2 10:31
 */
@Slf4j
public class WxAuthGatewayFilter extends AbstractGatewayFilter implements GatewayFilter, Ordered {

    /**
     * 缓存key名称不能随意修改，需要和授权模块的缓存key名称一致
     */
    private static final String BJWT_IN_SESSION_VALIDITY = "bjwt:wx:user:%s:clientType:%s";

    private GatewayCfgBean gatewayCfgBean;

    private RedissonService redissonService;

    public WxAuthGatewayFilter(RedissonService redissonService, GatewayCfgBean gatewayCfgBean) {
        this.redissonService = redissonService;
        this.gatewayCfgBean = gatewayCfgBean;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1、从header中获取bjwt
        ServerHttpRequest req = exchange.getRequest();
        HttpHeaders httpHeaders = req.getHeaders();
        ServerHttpRequest.Builder requestBuilder = req.mutate();

        String channel = httpHeaders.getFirst(CHANNEL_HEADER);
        if(!StringUtils.equals(channel, WEIXIN_CHANNEL_VALUE)) {
            // 非微信渠道不进行校验
            return chain.filter(exchange);
        }

        String bjwt = httpHeaders.getFirst(BJWT);
        if (StringUtils.isBlank(bjwt)) {
            log.warn("WxAuth 客户端请求业务接口未完成认证，不存在 {}", BJWT);
            return unauthorized(exchange);
        }

        Map<String, String> map;
        try {
            // 2、解析bjwt
            map = JwtUtil.decrypt(gatewayCfgBean.getMpJwtSecret(), bjwt);

            // 3、判断token里面是否有用户信息
            if (CollectionUtils.isEmpty(map) || !map.containsKey(USERID)) {
                log.warn("WxAuth 客户端请求业务接口未完成认证，JwtUtil.decrypt(bjwt) = {}", JSON.toJSONString(map));
                return methodNotAllowed(exchange);
            }

        } catch (TokenExpiredException e) {
            log.warn("WxAuth BJWT-已过期");
            return unauthorized(exchange);
        } catch (JWTDecodeException e) {
            log.error("WxAuth BJWT-非法：{}", e.getMessage(), e);
            return methodNotAllowed(exchange);
        } catch (Exception e){
            log.error("WxAuth BJWT-解析异常：{}", e.getMessage(), e);
            return methodNotAllowed(exchange);
        }

        String userId = map.get(USERID);

        if (!gatewayCfgBean.isSessionIntervalOff()) {
            try {
                // 3.1 判断会话间隔使用超过指定时间
                String clientType = httpHeaders.getFirst(CLIENT_TYPE_HEADER);
                if (StringUtils.isEmpty(clientType)) {
                    clientType = DEFAULT_CLIENT_TYPE;
                }
                String cacheKey = String.format(BJWT_IN_SESSION_VALIDITY, userId, clientType);
                String bjwtCache = redissonService.getStr(cacheKey);
                if (StringUtils.isEmpty(bjwtCache)) {
                    log.warn("WxAuth BJWT-已超时");
                    return unauthorized(exchange);
                } else if (!StringUtils.equals(bjwtCache, bjwt)) {
                    log.warn("WxAuth BJWT-非法：{}", bjwt);
                    return methodNotAllowed(exchange);
                } else {
                    redissonService.expire(cacheKey, Duration.ofSeconds(gatewayCfgBean.getSessionMaxInterval()));
                }
            } catch (Exception e) {
                log.warn("WxAuth 判断会话有效期异常：{}", e.getMessage(), e);
            }
        }

        // 4、封装用户信息
        LoginAccountDetail accountDetail = new LoginAccountDetail();
        ThirdUserInfo userInfo = new ThirdUserInfo();
        userInfo.setUserId(userId);
        accountDetail.setThirdUserInfo(userInfo);

        // 5、将用户信息放入请求中
        requestBuilder.header(USER_INFO, Base64.getEncoder().encodeToString(JSON.toJSONString(accountDetail).getBytes(StandardCharsets.UTF_8)));
        ServerHttpRequest serverHttpRequest = requestBuilder.build();

        // 6、继续后续调用
        return chain.filter(exchange.mutate().request(serverHttpRequest).build());
    }

    @Override
    public int getOrder() {
        return 14;
    }
}

